﻿#include "netif.h"
#include "ether.h"
#include "tools.h"
#include "protocol.h"
#include "arp.h"
#include "ipv4.h"

#if DBG_DISP_ENABLED(DBG_ETHER_LEVEL)
static void display_ether_pkt(char *title, ether_hdr_t *hdr, int total_size)
{
    plat_printf("--------------%s-------------\n", title);

    plat_printf("len: %d bytes\n", total_size);
    dbg_dump_hwaddr("dest: ", hdr->dest, ETHER_HWA_SIZE);
    dbg_dump_hwaddr("src: ", hdr->src, ETHER_HWA_SIZE);
    plat_printf("type: %04x ", x_ntohs(hdr->protocol));
    switch (x_ntohs(hdr->protocol))
    {
    case NET_PROTOCOL_ARP:
        plat_printf(" arp\n");
        break;
    case NET_PROTOCOL_IPV4:
        plat_printf(" ipv4\n");
        break;
    default:
        plat_printf(" unknown\n");
        break;
    }

    plat_printf("-----------------------------\n");
}
#else
#define display_ether_pkt(title, hdr, total_size)
#endif

/**
 * @brief 判断ether协议收到的包是否合法
 */
net_err_t ether_is_pkt_ok(ether_hdr_t *hdr, int total_size)
{
    if (total_size > ETHER_MTU + sizeof(ether_hdr_t))
    {
        DBG_WARNING(DBG_ETHER_LEVEL, "the pkt is too big than mtu in ether! size = %d!", total_size);
        return NET_ERR_SIZE;
    }

    if (total_size < sizeof(ether_hdr_t))
    {
        DBG_WARNING(DBG_ETHER_LEVEL, "the pkt is too small than hdr in ether! size = %d!", total_size);
        return NET_ERR_SIZE;
    }

    return NET_ERR_OK;
}

static net_err_t ether_open(netif_t *netif)
{
    net_err_t err = arp_make_gratuitous(netif);
    if (err < 0)
    {
        DBG_ERROR(DBG_ETHER_LEVEL, "send gratuitous arp failed!");
        return err;
    }

    return NET_ERR_OK;
}

static net_err_t ether_close(netif_t *netif)
{
    arp_clear(netif);
    return NET_ERR_OK;
}

static net_err_t ether_in(netif_t *netif, pktbuf_t *buf)
{
    DBG_INFO(DBG_ETHER_LEVEL, "ether in!");
    pktbuf_set_cont(buf, sizeof(ether_hdr_t));
    ether_hdr_t *hdr = (ether_hdr_t *)pktbuf_data(buf);

    if (ether_is_pkt_ok(hdr, buf->total_size) < NET_ERR_OK)
    {
        DBG_WARNING(DBG_ETHER_LEVEL, "the pkt of ether is error!");
        return NET_ERR_PARAM;
    }

    // todo...
    display_ether_pkt("ether pkt", hdr, buf->total_size);
    // 根据收到包的类型进行不同的处理
    switch (x_ntohs(hdr->protocol))
    {
    case NET_PROTOCOL_ARP:
    {
        // 移除包头，交给上一层处理
        net_err_t err = pktbuf_remove_header(buf, sizeof(ether_hdr_t));
        if (err < NET_ERR_OK)
        {
            DBG_ERROR(DBG_ETHER_LEVEL, "remove header in ether failed!");
            return NET_ERR_SYS;
        }

        return arp_in(netif, buf);
        break;
    }
    case NET_PROTOCOL_IPV4:
    {
        // 根据ipv4包更新arp表,以提高交互效率
        arp_update_from_ipbuf(netif, buf);

        net_err_t err = pktbuf_remove_header(buf, sizeof(ether_hdr_t));
        if (err < NET_ERR_OK)
        {
            DBG_ERROR(DBG_ETHER_LEVEL, "remove header in ether failed!");
            return NET_ERR_SYS;
        }
        return ipv4_in(netif, buf);
        break;
    }
    default:
        DBG_WARNING(DBG_ETHER_LEVEL, "the protocol: is not support!", x_ntohs(hdr->protocol));
        return NET_ERR_NOSUPPORT;
    }

    pktbuf_free(buf);

    return NET_ERR_OK;
}

/**
 * @brief ether对于数据包发送的处理
 * @param netif 发送网卡
 * @param dest 目的ip地址
 * @param buf 需要发送的数据包
 */
static net_err_t ether_out(netif_t *netif, ipaddr_t *dest, pktbuf_t *buf)
{
    // 自收自发
    if (ipaddr_is_equal(&netif->ipaddr, dest))
    {
        return ether_raw_out(netif, NET_PROTOCOL_IPV4, netif->hwaddr.addr, buf);
    }

    return arp_resolve(netif, dest, buf);
}

static const link_layer_t ether_link_layer = {
    .type = NETIF_TYPE_ETHER,
    .open = ether_open,
    .close = ether_close,
    .in = ether_in,
    .out = ether_out};

/**
 * @brief ether初始化相关
 */
net_err_t ether_init()
{
    DBG_INFO(DBG_ETHER_LEVEL, "ether init begin!");

    net_err_t err = netif_register_layer(NETIF_TYPE_ETHER, &ether_link_layer);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ETHER_LEVEL, "register layer failed in ether init!");
        return NET_ERR_SYS;
    }

    DBG_INFO(DBG_ETHER_LEVEL, "ether init end!");

    return NET_ERR_OK;
}

/**
 * @brief 返回以太网协议广播硬件地址
 */
const uint8_t *ether_broadcast_addr()
{
    // 广播地址：0xff 0xff 0xff 0xff 0xff 0xff
    // 多播地址：让数据包发送给多个机器
    // 单播地址：只根据地址发送给指定的机器
    static const uint8_t broadcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
    return broadcast;
}

/**
 * @brief ether向外发送数据包
 * @param netif 对应的网络接口
 * @param protocol ether协议类型
 * @param dest 目的硬件地址
 * @param buf 需要发送的数据包
 */
net_err_t ether_raw_out(netif_t *netif, uint16_t protocol, const uint8_t *dest, pktbuf_t *buf)
{
    if (!netif || !dest || !buf)
    {
        return NET_ERR_PARAM;
    }

    // 先检查数据包大小是否满足要求
    int size = buf->total_size;
    net_err_t err = NET_ERR_OK;
    if (size < ETHER_DATA_MIN)
    {
        DBG_INFO(DBG_ETHER_LEVEL, "the buf size:%d in ether too small!", size);

        err = pktbuf_resize(buf, ETHER_DATA_MIN);
        if (err < NET_ERR_OK)
        {
            DBG_WARNING(DBG_ETHER_LEVEL, "pkt resize failed in ether!");
        }
        pktbuf_reset_acc(buf);
        pktbuf_seek(buf, size);
        pktbuf_fill(buf, 0, ETHER_DATA_MIN - size);
        size = ETHER_DATA_MIN;
    }

    // 添加并初始化包头
    err = pktbuf_add_header(buf, sizeof(ether_hdr_t), true);
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ETHER_LEVEL, "add header failed in ether!");
        return err;
    }
    pktbuf_reset_acc(buf);
    ether_hdr_t *hdr = (ether_hdr_t *)pktbuf_data(buf);
    plat_memcpy(hdr->dest, dest, ETHER_HWA_SIZE);
    plat_memcpy(hdr->src, netif->hwaddr.addr, ETHER_HWA_SIZE);
    hdr->protocol = x_htons(protocol);

    display_ether_pkt("ether out", hdr, buf->total_size);

    // 这里要注意目的地址是否与源地址相同
    if (plat_memcmp(hdr->src, hdr->dest, ETHER_HWA_SIZE) == 0)
    {
        err = netif_put_in(netif, buf, DELIVER_NO_WAITTIME);
    }
    else
    {
        err = netif_put_out(netif, buf, DELIVER_NO_WAITTIME);
    }
    if (err < NET_ERR_OK)
    {
        DBG_ERROR(DBG_ETHER_LEVEL, "ether:%s send pkt failed!", netif->name);
        return err;
    }

    return NET_ERR_OK;
}